<!--
 * @moduleName: 下拉树组件
 * @Author: dawdler
 * @Date: 2018-12-19 14:03:03
 * @LastModifiedBy: dawdler
 * @LastEditTime: 2019-04-01 15:58:58
 -->
<template>
  <div class="el-tree-select">
    <!-- 下拉文本 -->
    <el-select
      ref="select"
      v-model="labels"
      v-popover:popover
      :collapse-tags="collapseTags"
      :clearable="clearable"
      style="width: 100%;"
      :style="styles"
      class="el-tree-select-input"
      :disabled="disabled"
      popper-class="select-option"
      v-bind="selectParams"
      :popper-append-to-body="false"
      :filterable="false"
      @remove-tag="_selectRemoveTag"
      @clear="_selectClearFun"
      @focus="_popoverShowFun"
      @visible-change="_visibleChange"
    />
    <!-- 弹出框 -->
    <el-popover
      ref="popover"
      v-model="visible"
      :disabled="disabled"
      :placement="placement"
      popper-class="el-tree-select-popper"
      :width="width"
      trigger="click"
    >
      <!-- 是否显示搜索框 -->
      <el-input
        v-if="treeParams.filterable"
        v-model="keywords"
        size="mini"
        class="input-with-select mb10"
        @change="_searchFun"
      >
        <!--                <el-button slot="append" icon="el-icon-search"></el-button>-->
      </el-input>
      <el-scrollbar
        tag="div"
        wrap-class="el-select-dropdown__wrap"
        view-class="el-select-dropdown__list"
        class="is-empty"
      >
        <!-- 树列表  @check="_treeCheckFun"   :default-expand-all="true"   check-strictly   @node-click="_treeNodeClickFun" :default-expanded-keys="defaultExpandedKeys" -->
        <el-tree
          v-show="data.length > 0"
          ref="tree"
          :default-expanded-keys="defaultExpandedKeys"
          :expand-on-click-node="false"
          :draggable="false"
          :current-node-key="ids.length > 0 ? ids[0] : ''"
          :filter-node-method="_filterFun"
          v-bind="treeParams"
          :data="data"
          :show-checkbox="selectParams.multiple"
          :highlight-current="true"
          :node-key="propsValue"
          :check-strictly="checkStrictly"
          @node-click="_treeNodeClickFun"
          @check="_treeCheckFun"
          @check-change="handleCheckChange"
        />
        <!-- 暂无数据 @check="handleCheck" @check-change="handleCheckChange"-->
        <div v-if="data.length === 0" class="no-data">暂无数据</div>
      </el-scrollbar>
    </el-popover>
  </div>
</template>
<style>
.el-tree-select .select-option {
  display: none !important;
}

.el-tree-select-popper {
  max-height: 400px;
  overflow: auto;
}

.el-tree-select-popper .el-button--small {
  width: 25px !important;
  min-width: 25px !important;
}

.el-tree-select-popper[x-placement^="bottom-start"] {
  margin-top: 5px;
}

.mb10 {
  margin-bottom: 10px;
}

.no-data {
  height: 32px;
  font-size: 14px;
  line-height: 32px;
  color: #ccc;
  text-align: center;
}
</style>
<script>
  import { on, off } from '@/components/ELTreeSelect/utils/dom'
  import { each } from '@/components/ELTreeSelect/utils/utils'
  // @group api
  export default {
    name: 'treeSelect',
    components: {},
    props: {
      checkStrictly: {
        type: Boolean,
        default: false
      },
      // v-model,存储的是treeParams.data里面的id
      value: {
        // `String` / `Array`
        type: [String, Number, Array],
        // `''`
        default() {
          return ''
        }
      },
      // el-select样式
      styles: {
        type: Object,
        // {}
        default() {
          return {}
        }
      },
      // 是否禁用文本框
      disabled: {
        type: Boolean,
        // false
        default() {
          return false
        }
      },
      clearable: {
        type: Boolean,
        // false
        default() {
          return true
        }
      },
      expandedLevel: {
        type: Number,
        default: 2
      },
      // 弹出框位置
      placement: {
        type: String,
        //  bottom
        default() {
          return 'bottom-start'
        }
      },
      popoverWidth: {
        type: [String, Number],
        default: ''
      },
      collapseTags: {
        type: Boolean,
        default: true
      },
      /*
        文本框参数，几乎支持el-select所有的API<br>
        取消参数：<br>
        设定下拉框的弹出框隐藏：<br>
        `:popper-append-to-body="false"` <br>
        搜索从弹出框里面执行： <br>
        `:filterable="false"`
        */
      selectParams: {
        type: Object,
        /*
            Object默认参数：<br><br>
            是否多选：<br>
            `multiple: true`<br><br>
            是否可以清空选项：<br>
            `clearable: true,`<br><br>
            是否禁用：<br>
            `disabled: false,`<br><br>
            搜索框placeholder文字：<br>
            `placeholder: '请选择',`<br><br>
            */
        default() {
          return {
            multiple: true,
            clearable: true,
            disabled: false,
            placeholder: '请选择'
          }
        }
      },
      /*
        下拉树参数，几乎支持el-tree所有的API<br>
         取消参数:<br>
        `:show-checkbox="selectParams.multiple"`<br>
        使用下拉框参数multiple判断是否对树进行多选<br>
        取消对el-tree的人为传参show-checkbox<br>
        `:node-key="propsValue"`     自动获取treeParams.props.value<br>
        `:draggable="false"`         屏蔽拖动
        */
      treeParams: {
        type: Object,
        /*
            Object默认参数：<br><br>
            在有子级的情况下是否点击父级关闭弹出框,false 只能点击子级关闭弹出框：`clickParent: false`<br><br>
            是否显示搜索框：<br>
            `filterable: false`<br><br>
            下拉树的数据：<br>
            `data:[]`<br><br>
            下拉树的props：<br>
            `props: {`<br>
                `children: 'children',`<br>
                `label: 'name',`<br>
                `value: 'flowId',`<br>
                `disabled: 'disabled'`<br>
            `}`
            */
        default() {
          return {
            clickParent: false,
            filterable: false,
            data: [],
            props: {
              children: 'children',
              label: 'name',
              value: 'flowId',
              disabled: 'disabled'
            }
          }
        }
      }
    },
    data() {
      return {
        propsValue: 'flowId',
        propsLabel: 'name',
        propsDisabled: 'disabled',
        propsChildren: 'children',
        data: [],
        keywords: '',
        labels: '', // 存储名称，用于下拉框显示内容
        ids: [''], // 存储id
        selectNodes: [''], // 选中数据
        visible: false, // popover v-model
        width: 150,
        treeData: [],
        defaultExpandedKeys: []
      }
    },
    watch: {
      data: {
        handler(val) {
          if (val && val.length > 0) {
            this._getExpandedKeys(val)
            // this.$nextTick(() => {
            //   this.$refs.tree.setCurrentKey(val[0][this.propsValue])
            // })
          }
        },
        immediate: true
      },
      ids: function(val) {
        if (val !== undefined) {
          this._setSelectNodeFun(val)
        }
      },
      // value: function(val) {
      //   if (this.ids !== val) {
      //     if (this.selectParams.multiple) {
      //       this.ids = val
      //     } else {
      //       this.ids = val === '' ? [] : [val]
      //     }
      //   }
      // },
      keywords(val) {
        this.$refs.tree.filter(val)
      },
      treeParams: {
        handler(val) {
          const { props, data } = val
          this.propsValue = props.value
          this.propsLabel = props.label
          this.propsDisabled = props.disabled
          this.propsChildren = props.children
          this.data = data.length > 0 ? [...data] : []
          if (props.defaultValue === 'orgSN' && data.length > 0) {
            this.ids.push(data[0].orgSN)
          }
        },
        deep: true,
        immediate: true
      }
    },
    created() {
      const { multiple } = this.selectParams
      if (multiple) {
        this.labels = []
        this.ids = this.value
      } else {
        this.labels = ''
        this.ids = this.value instanceof Array ? this.value : [this.value]
      }
    },
    mounted() {
      this._updateH()
      this.$nextTick(() => {
        on(document, 'mouseup', this._popoverHideFun)
      })
    },
    beforeDestroy() {
      off(document, 'mouseup', this._popoverHideFun)
    },
    methods: {
      // 输入文本框输入内容抛出
      _searchFun() {
        /*
            对外抛出搜索方法，自行判断是走后台查询，还是前端过滤<br>
            前端过滤：this.$refs.treeSelect.$refs.tree.filter(value);<br>
            后台查询：this.$refs.treeSelect.treeDataUpdateFun(data);
            */
        this.$emit('searchFun', this.keywords)
      },
      _getExpandedKeys(data, level = 1) {
        data.forEach(item => {
          if (level < this.expandedLevel) {
            if (this.defaultExpandedKeys.indexOf(item[this.propsValue]) === -1) {
              this.defaultExpandedKeys.push(item[this.propsValue])
            }

            if (item.children) {
              this._getExpandedKeys(item.children, level + 1)
            }
          }
        })
      },
      //  根据id筛选当前树名称，以及选中树列表
      _setSelectNodeFun(ids) {
        const el = this.$refs.tree
        if (!el) {
          throw new Error('找不到tree dom')
        }
        const { multiple } = this.selectParams
        // 长度为0，清空选择
        if (ids.length === 0 || this.data.length === 0) {
          this.labels = multiple ? [] : ''
          if (multiple) {
            el.setCheckedKeys([])
          } else {
            el.setCurrentKey('')
          }
          return
        }
        if (multiple) {
          el.setCheckedKeys(ids)
          this.labels = el.getCheckedNodes().map((item) => item[this.propsLabel]) || []
        } else {
          el.setCurrentKey(ids[0])
          if (el.getCurrentNode()) {
            this.labels = el.getCurrentNode()[this.propsLabel]
          } else {
            this.labels = ''
          }
        }
        this._updatePopoverLocationFun()
      },
      // 更新popover位置
      _updatePopoverLocationFun() {
        // dom高度还没有更新，做一个延迟
        setTimeout(() => {
          this.$refs.popover.updatePopper()
        }, 50)
      },
      // 树过滤
      _filterFun(value, data, node) {
        if (!value) return true
        return data[this.propsLabel].indexOf(value) !== -1
      },
      // 树点击
      _treeNodeClickFun(data, node, vm) {
        const { multiple } = this.selectParams
        const { clickParent } = this.treeParams
        const { propsValue, propsChildren, propsDisabled } = this
        if (data[propsDisabled]) {
          // 禁用
          return
        }
        if (node.checked) {
          const value = data[this.propsValue]
          this.ids = this.ids.filter((id) => id !== value)
        } else {
          if (!multiple) {
            // 多选，不关闭，单选，判断是否允许点击父级关闭弹出框
            if (!clickParent) {
              const children = data[propsChildren]
              // 如果不允许点击父级,自身为末级，允许点击之后关闭
              if (!children || children.length === 0) {
                this.ids = [data[propsValue]]
                this.visible = false
              } else {
                // 不允许父级，阻止继续派发
                return false
              }
            } else {
              this.ids = [data[propsValue]]
              this.visible = false
            }
          } else {
            this.ids.push(data[propsValue])
          }
        }
        this._emitFun()
        /*
            点击节点，对外抛出   `data, node, vm`<br>
            `data:` 当前点击的节点数据<br>
            `node:` 当前点击的node<br>
            `vm:` 当前组件的vm
            */
        this.$emit('node-click', data, node, vm)
      },
      // 树勾选
      _treeCheckFun(data, node, vm) {
        if (!this.checkStrictly) {
          this.ids = []
          const { propsValue } = this
          node.checkedNodes.forEach((item) => {
            this.ids.push(item[propsValue])
          })
          /*
            点击复选框，对外抛出   `data, node, vm`<br>
            `data:` 当前点击的节点数据<br>
            `node:` 当前点击的node<br>
            `vm:` 当前组件的vm
            */
          this.$emit('check', data, node, vm)
          this._emitFun()
        } else {
          this.handleCheck(data, node)
        // this.$emit("check", data, node, vm);
        //  this._emitFun();
        }
      },
      // 下拉框移除tag时触发
      _selectRemoveTag(tag) {
        const { data, propsValue, propsLabel, propsChildren } = this
        each(
          data,
          (item) => {
            if (item[propsLabel] === tag) {
              const value = item[propsValue]
              this.ids = this.ids.filter((id) => id !== value)
            }
          },
          propsChildren
        )
        this.$refs.tree.setCheckedKeys(this.ids)
        this._emitFun()
      },
      // 下拉框清空数据
      _selectClearFun() {
        this.ids = []
        const { multiple } = this.selectParams
        // 下拉框清空，对外抛出``this.$emit('input', multiple ? [] : '');`
        this.$emit('input', multiple ? [] : '')
        // 下拉框清空，对外抛出``this.$emit('select-clear');`
        this.$emit('select-clear')
        this._updatePopoverLocationFun()
      },
      _visibleChange() {
        if (this.treeParams.filterable) {
          // 显示时，清除搜索框内的值
          this.$emit('search-clear')
        }
      },
      // 判断类型，抛出当前选中id
      _emitFun() {
        const { multiple } = this.selectParams
        this.$emit('input', multiple ? this.ids : this.ids.length > 0 ? this.ids[0] : '')
        this._updatePopoverLocationFun()
      },
      // 更新宽度
      _updateH() {
        this.$nextTick(() => {
          if (this.popoverWidth) {
            this.width = this.popoverWidth
          } else {
            this.width = this.$refs.select.$el.getBoundingClientRect().width
          }
        })
      },
      // 显示弹出框的时候容错，查看是否和el宽度一致
      _popoverShowFun() {
        this._updateH()
      },

      // 判断是否隐藏弹出框
      _popoverHideFun(e) {
        const isInter = e.path.some((list) => {
          if (list.className && typeof (list.className) === 'string') {
            return list.className && list.className.indexOf('el-tree-select') !== -1
          } else {
            return false
          }
        })
        if (!isInter) {
          this.visible = false
        }
      },
      /**
       * @vuese
       * 树列表更新数据
       * @arg Array
       */
      treeDataUpdateFun(data) {
        this.data = data
        // 数据更新完成之后，判断是否回显内容
        setTimeout(() => {
          this._setSelectNodeFun(this.ids)
        }, 300)
      },

      /**
       * @vuese
       * 本地过滤方法
       * @arg String
       */
      filterFun(val) {
        this.$refs.tree.filter(val)
      },
      selectChildren(data) {
        data && data.children && data.children.map(item => {
          this.$refs.tree.setChecked(item[this.propsValue], true)
          if (data.children) {
            this.selectChildren(item)
          }
        })
      },
      handleCheck(data, { checkedKeys }) {
        // this.ids = checkedKeys
        // 节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
        // 如果为取消
        if (checkedKeys && checkedKeys.includes(data[this.propsValue])) {
          // 如果当前节点有子集
          this.selectChildren(data)
        }
      },
      handleCheckChange(data, checked, indeterminate) {
        if (!this.checkStrictly) return
        // 节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
        if (checked === false) {
          // 如果当前节点有子集
          if (data.children) {
            // 循环子集将他们的选中取消
            data.children.map(item => {
              this.$refs.tree.setChecked(item[this.propsValue], false)
            })
          }
        } else {
          // 否则(为选中状态)
          // 判断父节点id是否为空
          if (data.parentId !== 0) {
            // 如果不为空则将其选中
            this.$refs.tree.setChecked(data.parentId, true, true)
          }
        }
        var check = this.$refs.tree.getCheckedNodes()
        if (check && check.length) {
          this.ids = check.map(item => item[this.propsValue])
        } else {
          this.ids = []
        }
        this.$emit('checkData', this.ids)
      }
    }
  }
</script>
